// Copyright Epic Games, Inc. All Rights Reserved.

#pragma once

#include "zenstore.h"

#include <zencore/iohash.h>
#include <zencore/stats.h>
#include <zenstore/hashkeyset.h>
#include <zenutil/statsreporter.h>

#include <filesystem>

namespace zen {

class GcManager;
class CasStore;
class CompressedBuffer;
class IoBuffer;
class ScrubContext;
class WorkerThreadPool;

struct CidStoreSize
{
	uint64_t TinySize  = 0;
	uint64_t SmallSize = 0;
	uint64_t LargeSize = 0;
	uint64_t TotalSize = 0;
};

struct CidStoreStats
{
	uint64_t					  HitCount;
	uint64_t					  MissCount;
	uint64_t					  WriteCount;
	metrics::RequestStatsSnapshot AddChunkOps;
	metrics::RequestStatsSnapshot FindChunkOps;
	//	metrics::RequestStatsSnapshot ContainChunkOps;
};

struct CidStoreConfiguration
{
	// Root directory for CAS store
	std::filesystem::path RootDirectory;

	// Threshold below which values are considered 'tiny' and managed using the 'tiny values' strategy
	uint64_t TinyValueThreshold = 1024;

	// Threshold above which values are considered 'huge' and managed using the 'huge values' strategy
	uint64_t HugeValueThreshold = 1024 * 1024;
};

/** Content Store
 *
 * Data in the content store is referenced by content identifiers (CIDs), it works
 * with compressed buffers so the CID is expected to be the RAW hash. It stores the
 * chunk directly under the RAW hash.
 *
 */

class CidStore final : public ChunkResolver, public StatsProvider
{
public:
	CidStore(GcManager& Gc);
	~CidStore();

	struct InsertResult
	{
		bool New = false;
	};
	enum class InsertMode
	{
		kCopyOnly,
		kMayBeMovedInPlace
	};

	void					  Initialize(const CidStoreConfiguration& Config);
	InsertResult			  AddChunk(const IoBuffer& ChunkData, const IoHash& RawHash, InsertMode Mode = InsertMode::kMayBeMovedInPlace);
	std::vector<InsertResult> AddChunks(std::span<IoBuffer> ChunkDatas,
										std::span<IoHash>	RawHashes,
										InsertMode			Mode = InsertMode::kMayBeMovedInPlace);
	virtual IoBuffer		  FindChunkByCid(const IoHash& DecompressedId) override;
	bool					  IterateChunks(std::span<IoHash>												  DecompressedIds,
											const std::function<bool(size_t Index, const IoBuffer& Payload)>& AsyncCallback,
											WorkerThreadPool*												  OptionalWorkerPool);
	bool					  ContainsChunk(const IoHash& DecompressedId);
	void					  FilterChunks(HashKeySet& InOutChunks);
	void					  Flush();
	void					  ScrubStorage(ScrubContext& Ctx);
	CidStoreSize			  TotalSize() const;
	CidStoreStats			  Stats() const;

	virtual void ReportMetrics(StatsMetrics& Statsd) override;

private:
	struct Impl;
	std::unique_ptr<CasStore> m_CasStore;
	std::unique_ptr<Impl>	  m_Impl;
};

}  // namespace zen
